<?php

defined('SYSPATH') or die('Access error!');
/**
 * 路由器设置
 */
class Route {

	const REGEX_KEY     = '<([a-zA-Z0-9_]++)>';

	const REGEX_SEGMENT = '[^/.,;?\n]++';

	const REGEX_ESCAPE  = '[.\\+*?[^\\]${}=!|]';

	/**
	 * @var  默认的action
	 */
	public static $default_action = 'index';

	/**
	 * @var  array  路由对象数组
	 */
	protected static $_routes = array();

	/**
	 * 设置一个路由对象 例如：
	 *     Route::set('default', '(<controller>(/<action>(/<id>)))')
	 *         ->defaults(array(
	 *             'controller' => 'welcome',
	 *         ));
	 *
	 * @param   string   名字
	 * @param   string   模式
	 * @param   array    规则
	 * @return  Route
	 */
	public static function set($name, $uri, array $regex = NULL)
	{
		return Route::$_routes[$name] = new Route($uri, $regex);
	}

	/**
	 * 根据路由的名字获取一个路由对象.
	 *
	 *     $route = Route::get('default');
	 *
	 * @param   string  route name
	 * @return  Route
	 * @throws  Core_Exception
	 */
	public static function get($name)
	{
		if ( ! isset(Route::$_routes[$name]))
		{
			throw new Core_Exception('The requested route does not exist: :route',
				array(':route' => $name));
		}

		return Route::$_routes[$name];
	}

	/**
	 * 获取所有的路由
	 *
	 *     $routes = Route::all();
	 *
	 * @return  array  routes by name
	 */
	public static function all()
	{
		return Route::$_routes;
	}

	/**
	 *  得到路由的名字根据路由对象
	 *
	 *     $name = Route::name($route)
	 *
	 * @param   object  Route instance
	 * @return  string
	 */
	public static function name(Route $route)
	{
		return array_search($route, Route::$_routes);
	}

	/**
	 *  设置路由的缓存
         *  例如：
	 *
	 *     if ( ! Route::cache())
	 *     {
	 *         // Set routes here
	 *         Route::cache(TRUE);
	 *     }
	 *
	 * @param   boolean   cache the current routes
	 * @return  void      when saving routes
	 * @return  boolean   when loading routes
	 */
	public static function cache($save = FALSE)
	{
		if ($save === TRUE)
		{
			Core::cache('Route::cache()', Route::$_routes);
		}
		else
		{
			if ($routes = Core::cache('Route::cache()'))
			{
				Route::$_routes = $routes;

				return TRUE;
			}
			else
			{
				return FALSE;
			}
		}
	}

	/**
	 * 创建一个url根据路由的名字例如:
	 *
	 *     echo URL::site(Route::get($name)->uri($params), $protocol);
	 *
	 * @param   string   名字
	 * @param   array    参数
	 * @param   mixed    协议
	 * @return  string
	 */
	public static function url($name, array $params = NULL, $protocol = NULL)
	{
		return URL::site(Route::get($name)->uri($params), $protocol);
	}

	protected $_uri = '';

	protected $_regex = array();

	protected $_defaults = array('action' => 'index');

	protected $_route_regex;

	/**
	 * 创建路由
	 *     $route = new Route($uri, $regex);
	 *
	 * @param   string   route URI pattern
	 * @param   array    key patterns
	 * @return  void
	 * @uses    Route::_compile
	 */
	public function __construct($uri = NULL, array $regex = NULL)
	{
            
		if ($uri === NULL)
		{
			return;
		}

		if ( ! empty($regex))
		{
			$this->_regex = $regex;
		}

		$this->_uri = $uri;

		$this->_route_regex = $this->_compile();
	}

	/**
	 *  默认的路由
	 *
	 *     $route->defaults(array(
	 *         'controller' => 'welcome',
	 *         'action'     => 'index'
	 *     ));
	 *
	 * @param   array  key values
	 * @return  $this
	 */
	public function defaults(array $defaults = NULL)
	{
		$this->_defaults = $defaults;

		return $this;
	}

	/**
	 * 匹配uri
	 *
	 *     // Params: controller = users, action = edit, id = 10
	 *     $params = $route->matches('users/edit/10');
	 *
	 *
	 *     if ($params = $route->matches($uri))
	 *     {
	 *         // Parse the parameters
	 *     }
	 *
	 * @param   string  URI to match
	 * @return  array   on success
	 * @return  FALSE   on failure
	 */
	public function matches($uri)
	{
		if ( ! preg_match($this->_route_regex, $uri, $matches))
			return FALSE;
		$params = array();
		foreach ($matches as $key => $value)
		{
			if (is_int($key))
			{
				continue;
			}

			$params[$key] = $value;
		}

		foreach ($this->_defaults as $key => $value)
		{
			if ( ! isset($params[$key]) OR $params[$key] === '')
			{
				$params[$key] = $value;
			}
		}
		return $params;
	}

	/**
	 *  根据给的参数 获取一个uri.
	 *
	 *     // Using the "default" route: "users/profile/10"
	 *     $route->uri(array(
	 *         'controller' => 'users',
	 *         'action'     => 'profile',
	 *         'id'         => '10'
	 *     ));
	 *
	 * @param   array   URI parameters
	 * @return  string
	 */
	public function uri(array $params = NULL)
	{
		if ($params === NULL)
		{
			$params = $this->_defaults;
		}
		else
		{
			$params += $this->_defaults;
		}

		$uri = $this->_uri;

		if (strpos($uri, '<') === FALSE AND strpos($uri, '(') === FALSE)
		{
			return $uri;
		}

		while (preg_match('#\([^()]++\)#', $uri, $match))
		{
			$search = $match[0];

			$replace = substr($match[0], 1, -1);

			while (preg_match('#'.Route::REGEX_KEY.'#', $replace, $match))
			{
				list($key, $param) = $match;

				if (isset($params[$param]))
				{
					$replace = str_replace($key, $params[$param], $replace);
				}
				else
				{
					$replace = '';
					break;
				}
			}

			$uri = str_replace($search, $replace, $uri);
		}

		while (preg_match('#'.Route::REGEX_KEY.'#', $uri, $match))
		{
			list($key, $param) = $match;

			if ( ! isset($params[$param]))
			{
				throw new Core_Exception('Required route parameter not passed: :param',
					array(':param' => $param));
			}

			$uri = str_replace($key, $params[$param], $uri);
		}

		$uri = preg_replace('#//+#', '/', rtrim($uri, '/'));

		return $uri;
	}

	/**
	 * 返回一个编译的链接
	 *
	 *     $regex = $route->_compile();
	 *
	 * @return  string
	 */
	protected function _compile()
	{
		$regex = preg_replace('#'.Route::REGEX_ESCAPE.'#', '\\\\$0', $this->_uri);

		if (strpos($regex, '(') !== FALSE)
		{
			$regex = str_replace(array('(', ')'), array('(?:', ')?'), $regex);
		}

		$regex = str_replace(array('<', '>'), array('(?P<', '>'.Route::REGEX_SEGMENT.')'), $regex);

		if ( ! empty($this->_regex))
		{
			$search = $replace = array();
			foreach ($this->_regex as $key => $value)
			{
				$search[]  = "<$key>".Route::REGEX_SEGMENT;
				$replace[] = "<$key>$value";
			}

			$regex = str_replace($search, $replace, $regex);
		}
		return '#^'.$regex.'$#uD';
	}

}
